///
use crate::db::FUDB;
use crate::model::indicators;
use crate::model::indicators::cmf;
use crate::model::indicators::IndicatorResultWrap;
///
use crate::model::kline::Kline;
use crate::model::FudaRecord;
use anyhow::anyhow;
use anyhow::ensure;
use anyhow::Result;
use surrealdb::opt::PatchOp;
use surrealdb::sql::Id;
use surrealdb::sql::Thing;
use yata::core::Candle;
use yata::core::IndicatorConfig;
use yata::core::IndicatorInstance;
use yata::indicators::ChaikinMoneyFlowInstance;

#[derive(Debug)]
pub struct Cmf {
    cmf_instance: ChaikinMoneyFlowInstance,
}

impl Cmf {
    pub fn new(conf: &cmf::CMFConfig, init_kline: &Kline) -> Result<Self> {
        let cmf = yata::indicators::ChaikinMoneyFlow { size: conf.size };
        let candle: Candle = init_kline.into();
        let cmf_instance: ChaikinMoneyFlowInstance = cmf.init(&candle)?;
        Ok(Cmf { cmf_instance })
    }

    pub fn next(&mut self, kline: &Kline) -> cmf::CMF {
        Self::compute(&mut self.cmf_instance, kline)
    }

    pub fn repeat(&mut self, kline: &Kline) -> cmf::CMF {
        let mut cmf_instance = self.cmf_instance.clone();
        Self::compute(&mut cmf_instance, kline)
    }

    fn compute(cmf_instance: &mut ChaikinMoneyFlowInstance, kline: &Kline) -> cmf::CMF {
        let candle: Candle = kline.into();

        let idr: yata::core::IndicatorResult = cmf_instance.next(&candle);

        let idrw: IndicatorResultWrap = IndicatorResultWrap::from(&idr);

        let value = cmf::CMF {
            signal0: idrw.s0,
            main: idrw.v0,
        };

        return value;
    }
}
/// 更新指定K线数据的CMF,现在只能做全量计算
///
/// @param klines: K线数据
///
/// config: Cmf 设置
///
/// return 更新的记录数
///
pub async fn update_cmf(
    klines: &Vec<Kline>,
    config: Option<indicators::cmf::CMFConfig>
) -> Result<u32> {
    ensure!(klines.len() > 0, "klines is empty");
    let mut updates = 0u32;
    if let Some(fstk) = klines.first() {
        let cmf = match config {
            Some(conf) => yata::indicators::ChaikinMoneyFlow { size: conf.size },
            None => yata::indicators::ChaikinMoneyFlow::default(),
        };

        let mut cmf: yata::indicators::ChaikinMoneyFlowInstance = cmf.init(
            &(fstk.open, fstk.high, fstk.low, fstk.close, fstk.volume as f64)
        )?;

        for (i, kline) in klines.iter().enumerate() {
            if let Some(Thing { id: Id::String(kid_str), .. }) = &kline.id {
                let idr: yata::core::IndicatorResult = cmf.next(
                    &(kline.open, kline.high, kline.low, kline.close, kline.volume as f64)
                );

                println!("idr[{i}] close={} ==> {:?}", kline.close, idr);

                let idrw: IndicatorResultWrap = IndicatorResultWrap::from(&idr);

                let cmf = indicators::cmf::CMF {
                    signal0: idrw.s0,
                    main: idrw.v0,
                };

                let updated: Option<FudaRecord> = FUDB.update(("kline", kid_str))
                    .patch(PatchOp::replace("/cmf", cmf)).await
                    .map_err(|err| anyhow!(err))?;

                if let Some(_) = updated {
                    updates += 1;
                }
            }
        }
    }
    Ok(updates)
}

#[cfg(test)]
mod test {
    use crate::db;
    use crate::model::indicators;
    use crate::model::kline::Klt;
    use crate::model::kline::KLINE_COMMON_FIELDS;

    #[tokio::test]
    async fn test_update_cmf() {
        let _ = db::init().await.unwrap();

        let klt_vec = vec![
            Klt::M005,
            Klt::M015,
            Klt::M030,
            Klt::M060,
            Klt::DAY,
            Klt::WEEK,
            Klt::MONTH
        ];
        for klt in &klt_vec {
            let klines = db::kline
                ::select("SZ.KZZ.128136", None, Some(klt), Some("klts ASC"), 0, 10000).await
                .unwrap();
            let result = db::indicators::cmf::update_cmf(&klines, None).await;
            println!("{:?}", result);
            assert_eq!(result.is_ok(), true);
        }

        let result = db::kline::select(
            "SZ.KZZ.128136",
            None,
            Some(&Klt::M005),
            Some("klts ASC"),
            0,
            100
        ).await;
        if let Ok(klines) = &result {
            for (index, kline) in klines.iter().enumerate() {
                if index > 30 {
                    break;
                }
                // println!(
                //     "[{:02}], close:{:.2}=>cmf:{:?}",
                //     index, kline.close, kline.cmf,
                // );
            }
        }
    }
}
